package megrez.keycloak.providers.opt;

import java.io.IOException;
import java.util.Locale;

import javax.ws.rs.core.Response;

import org.eclipse.paho.client.mqttv3.MqttException;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.sessions.AuthenticationSessionModel;

import lombok.extern.slf4j.Slf4j;
import megrez.keycloak.common.Consts;
import megrez.keycloak.common.DesensitizeHelper;
import megrez.keycloak.common.MailMessage;
import megrez.keycloak.providers.mqtt.MqttService;
import megrez.keycloak.providers.mqtt.MqttUtils;

/**
 * 邮件认证器
 * 
 * @author Lucky Yang
 * @since 2.6.9
 */
@Slf4j
public class MailAuthenticator implements Authenticator {
	private static final String EMAIL_ATTRIBUTE = "email";
	private static final String CODE_AUTH_NOTE = "code";
	private static final String TTL_AUTH_NOTE = "ttl";

	@Override
	public void close() {
		// 没有实现
	}

	@Override
	public void authenticate(AuthenticationFlowContext context) {
		AuthenticatorConfigModel configModel = context.getAuthenticatorConfig();
		KeycloakSession session = context.getSession();
		UserModel user = context.getUser();
		final MailConfigProperties properties = MailConfigProperties.readConfigVar(configModel.getConfig());

		String mail = user.getFirstAttribute(EMAIL_ATTRIBUTE);

		int length = properties.getLength();
		int ttl = properties.getTtl();

		String code = SecretGenerator.getInstance().randomString(length, SecretGenerator.DIGITS);
		AuthenticationSessionModel authSession = context.getAuthenticationSession();
		authSession.setAuthNote(CODE_AUTH_NOTE, code);
		authSession.setAuthNote(TTL_AUTH_NOTE, Long.toString(System.currentTimeMillis() + (ttl * 1000L)));

		if (log.isDebugEnabled()) {
			log.debug("Email verification code [{}] is valid for [{}] seconds", code, ttl);
		}

		try {
			Locale locale = session.getContext().resolveLocale(user);

			final MailMessage message = MailUtils.buildMailMessage(code, ttl, mail, locale);
			sendToMqtt(session, message);

			context.challenge(context.form()
					.setAttribute("realm", context.getRealm())
					.setAttribute(EMAIL_ATTRIBUTE, DesensitizeHelper.email(mail))
					.setAttribute(TTL_AUTH_NOTE, ttl/60)
					.createForm(Consts.FTL_PAGE_OTP_MAIL));
		} catch (Exception e) {
			context.failureChallenge(AuthenticationFlowError.INTERNAL_ERROR,
					context.form().setError("mailAuthCodeNotSent", e.getMessage())
							.createErrorPage(Response.Status.INTERNAL_SERVER_ERROR));
		}

	}

	private void sendToMqtt(KeycloakSession session, MailMessage message) {
		if (log.isDebugEnabled()) {
			log.debug("Email verification [{}]", message);
		}

		final String topic = MailUtils.getMqttTopic(session.getContext().getRealm().getId());
		MqttService client = session.getProvider(MqttService.class);

		try {
			if (!client.isConnected()) {
				client.reconnect();
			}
			client.publish(topic, MqttUtils.buildJsonMqttMessage(message, 2, true));
		} catch (MqttException | IOException e) {
			log.error("Send message to MQTT server exception", e);
		}

	}

	@Override
	public void action(AuthenticationFlowContext context) {
		// 获取用户输入的验证码
		String enteredCode = context.getHttpRequest().getDecodedFormParameters().getFirst("code");

		AuthenticationSessionModel authSession = context.getAuthenticationSession();
		// 验证码
		String code = authSession.getAuthNote(CODE_AUTH_NOTE);
		// 过期时间
		String ttl = authSession.getAuthNote(TTL_AUTH_NOTE);

		if (code == null || ttl == null) {
			context.failureChallenge(AuthenticationFlowError.INTERNAL_ERROR,
					context.form().createErrorPage(Response.Status.INTERNAL_SERVER_ERROR));
			return;
		}

		// 验证码有效期判断
		boolean isValid = enteredCode.equals(code);
		if (isValid) {
			if (Long.parseLong(ttl) < System.currentTimeMillis()) {
				// 已过期(expired)
				context.failureChallenge(AuthenticationFlowError.EXPIRED_CODE,
						context.form().setError("mailAuthCodeExpired").createErrorPage(Response.Status.BAD_REQUEST));
			} else {
				// 有效的(valid)
				context.success();
			}
		} else {
			// 无效的(invalid)
			AuthenticationExecutionModel execution = context.getExecution();
			if (execution.isRequired()) {
				context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS,
						context.form().setAttribute("realm", context.getRealm())
								.setError("mailAuthCodeInvalid").createForm(Consts.FTL_PAGE_OTP_MAIL));
			} else if (execution.isConditional() || execution.isAlternative()) {
				context.attempted();
			}
		}

	}

	@Override
	public boolean requiresUser() {
		return true;
	}

	@Override
	public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
		boolean allow = user.getFirstAttribute(EMAIL_ATTRIBUTE) != null;
		if (!allow) {
			log.warn("User has not configured a email address: {}", user.getUsername());
		}
		return allow;
	}

	@Override
	public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
		// 没有实现
	}
}
